package network;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;



/**
 * @User: vitobo
 * @Date: 2024-08-15
 * @Description: Tcp服务器端 实现
 *
 * TcpEchoServer
 */
public class TcpEchoServer {
    private ServerSocket serverSocket = null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("启动服务器");
        // 此处 使用CachedThreadPool, 使用FixedThreadPool 不合适(线程数是不固定的)
        ExecutorService threadPool = Executors.newCachedThreadPool();

        while (true){
            // 使用这个 clientSocket和具体的客户端进行交流;
            Socket clientSocket = serverSocket.accept();

            // 此处使用多线程来处理
            // 这里的多线程版本的程序, 最大的问题就是 可能涉及到频繁申请释放线程;
//            Thread t = new Thread(()->{
//                processConnection(clientSocket);
//            });
//            t.start();

            // 使用线程池
            threadPool.submit(()->{
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }

    }

    // 使用这个方法来处理一个连接
    // 这一个连接 对应到一个客户端, 但是这里可能会涉及到多次交互
    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%s] 客户端上线!\n", clientSocket.getInetAddress().toString(),clientSocket.getPort());
        // 基于上述 socket对象 和 客户端进行通信
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            // 由于要处理多个请求和响应, 也是使用循环来进行
            while (true){
                // 1.读取请求
                Scanner scanner = new Scanner(inputStream);
                if(!scanner.hasNext()){
                    // 没有下个数据, 说明读完了(客户端关闭连接)
                    System.out.printf("[%s:%s] 客户端下线! \n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                    break;
                }

                // 注意: 此处使用 next() 是一直读取到换行符/空格/其它空白符结束, 但是最终返回结果里不包含 上述空白符
                String request = scanner.next();
                
                // 2.根据请求构造响应
                String response = process(request);

                // 3.返回响应结果
                //    OutputStream没有 write string 这样的功能, 可以把String里的字节数组拿出来,进行写入
                //    也可使用字符流来进行转换
                PrintWriter printWriter = new PrintWriter(outputStream);
                // 此处使用 println来写入, 让结果中有一个\n换行符, 方便对端来接收解析
                printWriter.println(response);
                // flush 用来刷新缓冲区, 保证当前写入的数据, 确实是发送出去了;
                printWriter.flush();

                System.out.printf("[%s:%d] req: %s; resp: %s \n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);
                
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 更合适的做法, 把close放到finally中, 保证一定能够执行到;
            clientSocket.close();
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}
